#include "httpform.h"
#include <arpa/inet.h>
// isgw 相关定义
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fstream>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "simple_log.h"

using namespace std;
static const string name("name");
static const string namevalue("namevalue");
static const string filename("filename");
static const string filepath("filepath");

int HttpForm::recv(int fd,const string& filedir)
{
    if (fd < 0)
    {
        return -1;
    }

    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    setRecvTimeout(fd,timeout);

    const int buflen = 2048;
    char buf[buflen] = {0};

    /// under EPOLLET mode,the fd is nonblocked 
    int len = noblock_recv(fd,buf,buflen,1);
    if (len <= 0 )
    {
        return 0;
    }

    string http_head(buf,len);
    string http_head_end("\r\n\r\n");

    size_t head_end_pos = http_head.find(http_head_end);
    if ( head_end_pos != string::npos )
    {
        http_head = http_head.substr(0,head_end_pos + http_head_end.length());
    }
    else
    {
        // http head too large
        return -1;
    }

    ParseHeader(http_head);
    map<string,string>::iterator ithead;

    string contenttype;
    string contentlen("content-length");
    size_t bodylen = 0;

    ithead = m_head.find(contentlen);
    if ( ithead != m_head.end())
        bodylen = atoi(ithead->second.c_str());

    ithead = m_head.find("content-type");
    if ( ithead != m_head.end())
        contenttype = ithead->second;

    if ( bodylen <= 0 || contenttype.empty())
        return len;

    size_t headbodylen = len - http_head.length();
    char* pbuf = new (std::nothrow) char[bodylen];
    if ( pbuf == NULL )
        return -1;

    memcpy(pbuf, buf + http_head.length(), headbodylen);
    int leftlen = bodylen - headbodylen;
    if ( leftlen > 0 )
    {
        len = noblock_recv(fd, pbuf + headbodylen,leftlen,10);
        if ( len != leftlen )
        {
            ERROR_LOG("recv form body failed: bodylen:%d,leftlen:%d,recv len:%d\n",bodylen,leftlen,len);
            return len + http_head.length();
        }
    }
    
    DEBUG_LOG("HTTP body len:%d, leftlen:%d,recv len:%d\n",bodylen,leftlen,len);

    ParseBody(contenttype,pbuf,bodylen,filedir);
    delete [] pbuf;

    return len + http_head.length();
}

void HttpForm::ParseHeader(const string& headstr)
{
    vector<string> vhead;
    stringSplit(headstr,"\r\n",vhead);

    // 根据第一行计算 url
    if ( vhead.size() > 0 )
    {
        string& url = vhead[0];
        vector<string> vurl;
        stringSplit(url," ",vurl);
        if ( vurl.size() >= 2 )
            m_url = to_lower(vurl[1]);
    }
    vector<string>::iterator it;
    for( it = vhead.begin(); it != vhead.end(); ++it)
    {
        vector<string> voption;
        stringSplit(*it,":",voption);
        if ( voption.size() == 2 )
            m_head[to_lower(voption[0])] = voption[1];
    }
}

map<string,string> HttpForm::ReadOnePart(const string& onePart,const string& dir)
{
    // 普通field具有 dispostion,name字段
    // string disposition("Content-Disposition");
    // filename 在上传文件时有
    // 上传 .jpg,.png时，有contentType字段
    // png时，文件内容有PNG前缀，jpg没有
    // form: boundary=aaa
    // --aaa\r\n
    // \r\nvalue\r\n
    // --aaa\r\n
    // \r\nvalue2\r\n
    // --aaa--
    // 结束时--aaa--
    size_t namePos=0,filenamePos=0;

    map<string,string>  mapPart;
    // 解析 name，name值由引号包括
    namePos         = onePart.find(name);
    filenamePos     = onePart.find(filename);
    if (namePos != string::npos)
    {
        size_t valueBegin = onePart.find("\"",namePos);
        size_t valueEnd   = onePart.find("\"",valueBegin + 1);

        if (valueBegin != string::npos && valueEnd != string::npos)
        {
            string value = onePart.substr(valueBegin + 1,valueEnd - valueBegin - 1);
            mapPart[name] = value;
            // 取值  \r\n\r\nvalue,传文件时，也解析name的取值
            size_t valueBeginTmp = onePart.find("\r\n\r\n",valueEnd);
            if ( valueBeginTmp + 4 < onePart.length())
                mapPart[namevalue] = onePart.substr(valueBeginTmp+4);
            else
                mapPart[namevalue] = "";
        }
    }

    // 解析filename
    if (filenamePos != string::npos)
    {
        size_t valueBegin = onePart.find("\"",filenamePos);
        size_t valueEnd   = onePart.find("\"",valueBegin + 1);

        string strFileName = onePart.substr(valueBegin + 1,valueEnd - valueBegin - 1);
        mapPart[filename] = strFileName;
    
        // 获取ContentType 及 文件内容
        size_t contentTypeEnd = onePart.find("\r\n\r\n",valueEnd + 1);
        // onePart 结尾不带 \r\n了
        const string& content = onePart.substr(contentTypeEnd + 4);

        string path = dir;
        if ( path.empty() )
            path = "./";
        else if (path[path.length() - 1] != '/')
            path += "/";

        fstream file;
        path += strFileName;
        file.open(path.c_str(),fstream::out|fstream::trunc);
        file << content;
        file.close();
        mapPart[filepath] = path;
    }
    return mapPart;
}

void HttpForm::ParseBody(const string& contenttype,const char* body,int bodylen, const string& dir)
{
    // 解析 CONTENT_TYPE=multipart/form-data; boundary=----WebKitFormBoundaryKDFQb7SLBAYPrf5P
    string boundary;
    size_t pos = contenttype.find_last_of("=");
    boundary = contenttype.substr(pos+1);
    boundary = string("\r\n--") + boundary;
    
    string bodyStr(body,bodylen);
    vector<string> formFields;
    stringSplit(bodyStr,boundary,formFields);

    for (vector<string>::iterator itField = formFields.begin(); itField != formFields.end(); ++itField)
    {
        if (itField->empty())
            continue;
        map<string,string> tmpPart = ReadOnePart(*itField,dir);
        // name : input name
        // namevalue: input value
        // filename: image1.jpg
        // filepath: ./image1.jpg

        map<string,string>::iterator it;
        it = tmpPart.find(filename);
        if ( it != tmpPart.end()) // file part
            m_files[it->second] = tmpPart[filepath];
        else    // other field part
            m_fields[tmpPart[name]] = tmpPart[namevalue];
    }
}

int HttpForm::noblock_send(int fd, const char* buf, int len,unsigned int timeout)
{
    int totalsend = 0;
    int sendlen   = 0;
    struct timeval tstart;
    struct timeval tnow;
    if ( timeout > 0 )
        gettimeofday(&tstart,NULL);
    while(totalsend < len)
    {
        if ( timeout > 0 )
            gettimeofday(&tnow,NULL);

        sendlen = ::send(fd, buf + totalsend,len - totalsend, 0);
        if ( sendlen < 0 )
        {
            if ( timeout > 0 )
            {
                unsigned int msec = 1000*(tnow.tv_sec - tstart.tv_sec) + (tnow.tv_usec - tstart.tv_usec)/1000;
                if ( msec >= timeout )
                    break;
            }

            if ( errno == EINTR)
                continue;
            else
                break;
        }
        if ( timeout > 0 )
            tstart = tnow;
        totalsend   += sendlen;
    }

    return totalsend;
}

int HttpForm::noblock_recv(int fd, char* buf,int len,unsigned timeout)
{
    int total = 0;
    int recvlen = 0;
    struct timeval tstart;
    struct timeval tnow;
    if ( timeout > 0 )
        gettimeofday(&tstart,NULL);
    while( total < len )
    {
        if ( timeout > 0 )
            gettimeofday(&tnow,NULL);

        recvlen = ::recv(fd,buf + total, len - total, 0);
        if ( recvlen < 0)
        {
            if ( timeout > 0 )
            {
                unsigned int msec = 1000*(tnow.tv_sec - tstart.tv_sec) + (tnow.tv_usec - tstart.tv_usec)/1000;
                if ( msec >= timeout )
                    break;
            }

            if ( errno == EINTR)
                continue;
            else
                break;
        }
        else if (recvlen == 0)
            break;

        if ( timeout > 0 )
            tstart = tnow;
        total += recvlen;
    }

    return total;
}

int HttpForm::stringSplit(const string& str, const string& sep, vector<string> &vec )
{
    if (str.empty() || sep.empty())
        return 0;

    size_t posBegin = 0;
    size_t posEnd = str.find(sep,posBegin);
    while(posEnd != string::npos)
    {
        vec.push_back(str.substr(posBegin,posEnd-posBegin));
        posBegin = posEnd + sep.length();
        posEnd = str.find(sep,posBegin);
    }
    if (posBegin < str.length() )
        vec.push_back(str.substr(posBegin));
    else if (posBegin >= str.length() )
        vec.push_back("");

    return vec.size();
}

void HttpForm::setRecvTimeout(int fd,timeval& tval)
{
    if ( fd < 0 )
        return;
    ::setsockopt(fd,SOL_SOCKET, SO_RCVTIMEO, &tval,sizeof(tval));
}












